/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package abricots.net.common;

import abricots.MyLog;
import com.esotericsoftware.kryonet.Connection;
import com.esotericsoftware.kryonet.Listener;
import com.esotericsoftware.minlog.Log;
import java.util.ArrayDeque;
import java.util.Queue;

/**
 * First level Paquet Processor.
 * Handles paquets ID & stuff
 * @author charly
 */
public class PaquetProcessor extends Listener { //implements Listener to automatically processIn any message

    private final MyLog log = new MyLog(getClass().getSimpleName(), Log.LEVEL_DEBUG);
    private int lastSentId;
    private int lastReceivedId;
    private int lastReceivedAckId;
    private int lastReceivedReliableId;
    private Queue<GameMessage> reliablePaquets;
    private final ConnectionProvider connectionProvider;

    public PaquetProcessor(ConnectionProvider provider) {
        lastSentId = 0;
        lastReceivedId = -1;
        lastReceivedAckId = -1;
        lastReceivedReliableId = -1;
        reliablePaquets = new ArrayDeque<GameMessage>();
        this.connectionProvider = provider;
    }

    public GameMessage processOut(Object paquet) {
        if (GameMessage.class.isAssignableFrom(paquet.getClass())) {
            GameMessage message = (GameMessage) paquet;
            message.ackId = lastReceivedId;
            message.relAckId = lastReceivedReliableId;
            message.id = lastSentId++;
            log.trace("Message processed out : " + message);
            return message;
        }
        return null;
    }

    private GameMessage processIn(Object paquet) {
        if (GameMessage.class.isAssignableFrom(paquet.getClass())) {
            GameMessage message = (GameMessage) paquet;
            // Discard out of order messages
            if (message.id < lastReceivedId) {
                log.trace("Discarding out of order message: " + message);
                return null;
            }
            lastReceivedId = message.id;
            if (message.reliable) {
                if (message.id > lastReceivedReliableId) {
                    lastReceivedReliableId = message.id;
                } else {
                    log.trace("Old reliable message : " + message);
                    return null;
                }
            }
            if (!reliablePaquets.isEmpty()) {
                int lastSentReliableId = reliablePaquets.peek().id;
                if (message.relAckId == lastSentReliableId) {
                    reliablePaquets.remove();
                }
            }
            log.trace("Message processed in : " + message);
            return message;
        }
        return null;
    }

    public int getLastReceivedAckId() {
        return lastReceivedAckId;
    }

    @Override
    public void connected(Connection connection) {
        connectionProvider.onConnection(connection);
    }

    @Override
    public void disconnected(Connection connection) {
        connectionProvider.onDisconnection(connection);
    }

    @Override
    public void received(Connection connection, Object object) {
        GameMessage message = processIn(object);
        if (message != null) {
            connectionProvider.onMessageReceived(message);
        }
    }
}
